package com.sh.d1.gameserver.login;

import com.alibaba.fastjson.JSONObject;
import com.sh.d1.gameserver.MySqlSessionFactory;
import com.sh.d1.gameserver.async.AsyncOperationProcessor;
import com.sh.d1.gameserver.async.IAsyncOperation;
import com.sh.d1.gameserver.login.db.IUserDao;
import com.sh.d1.gameserver.login.db.UserEntity;
import org.apache.ibatis.session.SqlSession;
import com.sh.util.RedisUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import redis.clients.jedis.Jedis;

import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Function;

/**
 * 登录服务
 */
public final class LoginService {
    /**
     * 日志对象
     */
    static private final Logger LOGGER = LoggerFactory.getLogger(LoginService.class);

    /**
     * 单例对象
     */
    static private final LoginService _instance = new LoginService();

    /**
     * 私有化类默认构造器
     */
    private LoginService() {
    }

    /**
     * 获取单例对象
     *
     * @return 登录服务
     */
    static public LoginService getInstance() {
        return _instance;
    }

    /**
     * 用户登录
     *
     * @param userName 用户名称
     * @param password 用户密码
     * @param callback 回调函数
     */
    public void userLogin(String userName, String password, final Function<UserEntity, Void> callback) {
        if (null == userName ||
                null == password) {
            return;
        }

        IAsyncOperation asyncOp = new AsyncGetUserEntity(userName, password) {
            @Override
            public void doFinish() {
                if (null != callback) {
                    callback.apply(this.getUserEntity());
                }
            }
        };

        // 执行异步操作
        AsyncOperationProcessor.getInstance().process(asyncOp);
    }

    /**
     * 更加用户 Id 更新英雄形象
     *
     * @param userId     用户 Id
     * @param heroAvatar 英雄形象
     */
    public void updateHeroAvatarByUserId(int userId, String heroAvatar, final Function<Boolean, Void> callback) {
        if (userId <= 0 ||
                null == heroAvatar) {
            return;
        }

        IAsyncOperation asyncOp = new AsyncUpdateHeroAvatar(userId, heroAvatar) {
            @Override
            public void doFinish() {
                if (null != callback) {
                    callback.apply(true);
                }
            }
        };

        // 执行异步操作
        AsyncOperationProcessor.getInstance().process(asyncOp);
    }

    /**
     * 根据用户id获取已经登录用户
     * @param userId  用户id
     * @param callback  回调函数
     */
    public void getLoginUser(int userId, final Function<UserEntity, Void> callback) {
        if (userId <= 0 || null == callback) {
            return;
        }
        AtomicReference<UserEntity>   userEntityRef=new AtomicReference<>();
        AsyncOperationProcessor.getInstance().process(userId,
                //执行异步操作
                () -> {
                    try (Jedis redis = RedisUtil.getRedis()) {


                        // 更新用户基本信息
                        String jsnStr = redis.hget("User_" + userId, "BasicInfo");
                        if (null == jsnStr || !jsnStr.startsWith("{")) {
                            return;
                        }
                        JSONObject jsonObj = JSONObject.parseObject(jsnStr);
                        userEntityRef.set(jsonObj.toJavaObject(UserEntity.class));

                    } catch (Exception ex) {
                        LOGGER.error(ex.getMessage(), ex);
                    }
                },
                //回到主线程
                () -> {
                     callback.apply(userEntityRef.get());
                }
        );
    }

    /**
     * 修改 Redis 中的用户数据
     *
     * @param userEntity 用户实体
     */
    private void updateUserBasicInfoInRedis(UserEntity userEntity) {
        if (null == userEntity) {
            return;
        }

        try (Jedis redis = RedisUtil.getRedis()) {
            // 获取用户 Id
            int userId = userEntity.userId;

            JSONObject jsonObj = new JSONObject();
            jsonObj.put("userId", userId);
            jsonObj.put("userName", userEntity.userName);
            jsonObj.put("heroAvatar", userEntity.heroAvatar);

            // 更新用户基本信息
            redis.hset("User_" + userId, "BasicInfo", jsonObj.toJSONString());
        } catch (Exception ex) {
            LOGGER.error(ex.getMessage(), ex);
        }
    }

    /**
     * 异步方式获取用户实体
     */
    static private class AsyncGetUserEntity implements IAsyncOperation {
        /**
         * 用户名称
         */
        private final String _userName;

        /**
         * 密码
         */
        private final String _password;

        /**
         * 用户实体
         */
        private UserEntity _userEntity = null;

        /**
         * 类参数构造器
         *
         * @param userName 用户名称
         * @param password 密码
         */
        AsyncGetUserEntity(String userName, String password) {
            this._userName = userName;
            this._password = password;
        }

        /**
         * 获取用户实体
         *
         * @return 用户实体
         */
        UserEntity getUserEntity() {
            return this._userEntity;
        }

        @Override
        public int getBindId() {
            return _userName.charAt(_userName.length() - 1);
        }

        @Override
        public void doAsync() {
            try (SqlSession mySqlSession = MySqlSessionFactory.openSession()) {
                // 获取用户 DAO
                IUserDao userDao = mySqlSession.getMapper(IUserDao.class);
                // 根据用户名称获取实例
                UserEntity userEntity = userDao.getByUserName(this._userName);

                if (null != userEntity) {
                    if (!_password.equals(userEntity.password)) {
                        LOGGER.error(
                                "用户密码错误, userName = {}",
                                _userName
                        );

                        throw new RuntimeException("用户密码错误");
                    }
                } else {
                    // 如果是未找到用户,
                    // 则新建用户
                    userEntity = new UserEntity();
                    userEntity.userName = _userName;
                    userEntity.password = _password;

                    userDao.insertInto(userEntity);
                }

                LOGGER.info(
                        "用户登录成功, userId = {}, userName = {}",
                        userEntity.userId,
                        userEntity.userName
                );

                // 更新 Redis 中的用户基本信息
                LoginService.getInstance().updateUserBasicInfoInRedis(userEntity);

                _userEntity = userEntity;
            } catch (Exception ex) {
                // 记录错误日志
                LOGGER.error(ex.getMessage(), ex);
            }
        }
    }

    /**
     * 异步方式更新英雄形象
     */
    static private class AsyncUpdateHeroAvatar implements IAsyncOperation {
        /**
         * 用户 Id
         */
        private final int _userId;

        /**
         * 英雄形象
         */
        private final String _heroAvatar;

        /**
         * 类参数构造器
         *
         * @param userId     用户 Id
         * @param heroAvatar 英雄形象
         */
        AsyncUpdateHeroAvatar(int userId, String heroAvatar) {
            this._userId = userId;
            this._heroAvatar = heroAvatar;
        }

        @Override
        public int getBindId() {
            return _userId;
        }

        @Override
        public void doAsync() {
            try (SqlSession mySqlSession = MySqlSessionFactory.openSession()) {
                // 获取用户 DAO
                IUserDao userDao = mySqlSession.getMapper(IUserDao.class);
                // 更新英雄形象
                userDao.updateHeroAvatarByUserId(
                        this._userId,
                        this._heroAvatar
                );

                // 获取用户实体
                UserEntity userEntity = userDao.getByUserId(this._userId);
                // 更新 Redis 中的用户基本信息
                LoginService.getInstance().updateUserBasicInfoInRedis(userEntity);
            } catch (Exception ex) {
                // 记录错误日志
                LOGGER.error(ex.getMessage(), ex);
            }
        }
    }
}
